package net.towee.client.com.stuff.view;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.towee.client.com.stuff.presenter.GroupsCreatorPresenter.Display;
import net.towee.client.i18n.ToweeConstants;
import net.towee.client.resources.ClientResource;
import net.towee.client.widgets.ExtFlowPanel;
import net.towee.client.widgets.ListItemWidget;
import net.towee.client.widgets.SheduledInputBox;
import net.towee.client.widgets.SpanPanel;
import net.towee.client.widgets.UnorderedListWidget;
import net.towee.model.account.AccountPermission;
import net.towee.model.account.Role;
import net.towee.model.account.impl.RoleImpl;
import net.towee.model.group.GroupOpenness;
import net.towee.model.utils.Pair;

import com.google.common.base.Preconditions;
import com.google.gwt.animation.client.Animation;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.inject.Inject;

public class GroupsCreatorView extends FlowPanel implements Display {
	
	private class RoleEditor extends FlowPanel implements ClickHandler {
		private final RolesContainer container;
		private final HasText roleItem;
		private final Button createRole = new Button();
		private final CheckBox defaultBox = new CheckBox();
		private final SheduledInputBox roleNameBox = new SheduledInputBox();
		private final FlexTable table = new FlexTable();
		private Role role;
		
		private Map<AccountPermission, CheckBox> permBoxes = new HashMap<AccountPermission, CheckBox>();

		public RoleEditor(RolesContainer rolesContainer) {
			this(rolesContainer, null, null);
		}

		public RoleEditor(RolesContainer container, Role r, HasText rItem) {
			this.container = container;
			this.roleItem = rItem;
			this.role = r;
			
			if(defaultRole.equals(r))
				defaultBox.setValue(true);
			else
				defaultBox.setValue(false);
			
			// Create name field
			if(rItem != null)
				roleNameBox.setText(r.getName());
			add( createField(constants.roleName(), roleNameBox) );
			
			createDataForm();
			
			// Create permissions
			createPermissions();
			
			// Create button
			if(role == null) {
				createRole.setText(constants.create());
				createRole.setStyleName(resource.css().lobbyNextStepButton());
				add(createRole);
				createRole.addClickHandler(this);
			}	else {
				roleNameBox.addChangeHandler(new ChangeHandler(){
					@Override
					public void onChange(ChangeEvent event) {
						role.setName(roleNameBox.getText().trim());
						roleItem.setText(roleNameBox.getText().trim());						
					}
				});
			}
		}
		
		private void createDataForm() {
			// Create table
			FlexCellFormatter cellFormatter = table.getFlexCellFormatter();
			table.addStyleName( resource.css().dataForm() );
			table.setCellSpacing(6);		
			
			// Create table
			//table.setWidget(0, 0, getRowName( constants.roleName(), ""));
			//table.setWidget(0, 1, roleNameBox);
			table.setWidget(0, 0, getRowName( constants.isDefaultRole(), constants.isDefaultRoleInfo()));
			table.setWidget(0, 1, defaultBox);
			cellFormatter.setWidth(0, 1, "100%");
			
			add(table);
		}
		
		private FlowPanel getRowName(String name, String desc){
			FlowPanel row = new FlowPanel();
			
			ExtFlowPanel nameSpan = new ExtFlowPanel(name);
			ExtFlowPanel descSpan = new ExtFlowPanel(desc);

			nameSpan.getElement().setAttribute("row", "name");
			descSpan.getElement().setAttribute("row", "desc");
			
			row.add(nameSpan);
			row.add(descSpan);
			
			return row;
		}

		private void createPermissions() {	
			table.getFlexCellFormatter().setColSpan(1, 0, 2);
			Label permLabel = new Label( constants.permissions() );
			permLabel.setStyleName( resource.css().lobbyGroupCreatorPermLabel() );
			table.setWidget(1, 0, permLabel);
			int i = 2;
			
			for(final AccountPermission p : AccountPermission.values()) {
				final CheckBox box = new CheckBox();
				permBoxes.put(p, box);
				
				if(role != null) {
					if(role.hasPermissions(p) ) {
						box.setValue(true);
					}
					if(role.isOwner()) {
						box.setEnabled(false);
					}	else {
						box.addClickHandler(new ClickHandler(){
							@Override
							public void onClick(ClickEvent event) {
								if (box.getValue()) {
									role.addPermission(p);
								} else {
									role.removePremission(p);
								}								
							}
						});
					}
				}
				
				table.setWidget(i, 1, box);
				try {
					table.setWidget(i, 0, getRowName( constants.getString( p.toString() ), ""));
				} catch(Exception e) {
					table.setWidget(i, 0, getRowName( p.toString(), ""));
				} 
				
				i++;
			}
		}

		@Override
		public void onClick(ClickEvent event) {
			if (!roleNameBox.getText().trim().isEmpty()) {

				// Update or create role
				if (roleItem == null) {
					role = new RoleImpl((Random.nextDouble() + new Date().getTime()) + "", roleNameBox
							.getText().trim());
					container.addRole(role);
				} else {
					role.setName(roleNameBox.getText().trim());
					roleItem.setText(roleNameBox.getText().trim());
				}
				
				if(defaultBox.getValue())
					defaultRole = role;

				// Set permissions
				for (Entry<AccountPermission, CheckBox> e : permBoxes.entrySet()) {
					if (e.getValue().getValue()) {
						role.addPermission(e.getKey());
					} else {
						role.removePremission(e.getKey());
					}
				}

				// Remove editor
				removeFromParent();
				container.toggleSelection();
			}
		}
	}
	
	private class RolesContainer extends FlowPanel implements ClickHandler {
		private class SlideDownAnimation extends Animation {
			private boolean showed = true;
			private int height;
			
			public SlideDownAnimation() {
				height = roles.getElement().getOffsetHeight();
			}
			
			@Override
			protected void onStart(){
				if(showed) height = roles.getElement().getOffsetHeight();
				super.onStart();
			}			
			
			@Override
			protected void onComplete(){
				super.onComplete();
				showed = !showed;
				if(showed) roles.setHeight("auto");
			}

			@Override
			protected void onUpdate(double progress) {
				progress = (showed)?1-progress:progress;
				roles.setHeight(progress*height+"px");
			}
			
			
			public boolean isShowed() {
				return showed;
			}
		}
		
		private final ExtFlowPanel label = new ExtFlowPanel();
		private final FlowPanel roles = new FlowPanel();
		private final Button addRole = new Button();
		private final FlowPanel arrow = new FlowPanel();
		private SlideDownAnimation animation;
		
		private List<Role> rolesList = new ArrayList<Role>();
		
		public RolesContainer() {
			add(label);
			add(roles);
			add(arrow);
			
			animation = new SlideDownAnimation();
			roles.getElement().getStyle().setOverflow(Overflow.HIDDEN);
			getElement().getStyle().setPosition(Position.RELATIVE);
			getElement().getStyle().setTop(10, Unit.PX);
			
			arrow.setVisible(false);
			arrow.setStyleName(resource.css().lobbyGroupCreationAreaArrow());
			label.setStyleName(resource.css().lobbyGroupCreationArea());
			label.setText( constants.roles() );
			addRole.setText( constants.addRole() );
			roles.setWidth("100%");
			addRole.setStyleName(resource.css().lobbyGroupCreationAddRole());
			label.add(addRole);
			
			addRole.addClickHandler(this);
			label.addClickHandler(this);
		}

		@Override
		public void onClick(ClickEvent event) {
			event.stopPropagation();
			if(event.getSource().equals( label ))
				animation.run(350);
			else {
				showRoleEditor( new RoleEditor(this) );
			}
		}

		private void showRoleEditor(RoleEditor editor) {
			label.addStyleName(resource.css().lobbyGroupCreationAreaActive());
			arrow.setVisible(true);
			showRightColumn( editor );
		}		
		
		public void toggleSelection() {
			label.removeStyleName(resource.css().lobbyGroupCreationAreaActive());
			arrow.setVisible(false);
		}
		
		public void addRole(final Role r) {
			addRole(r, true);
		}
		public void addRole(final Role r, boolean mayDeleted) {
			Preconditions.checkNotNull(r, "null role");
			
			if(!animation.isShowed())
				animation.run(350);
			
			// Add to list
			rolesList.add(r);
			
			// Create role item
			final ExtFlowPanel role = new ExtFlowPanel();
			final SpanPanel roleItem = new SpanPanel(r.getName());
			role.setStyleName(resource.css().lobbyGroupCreationRoleItem());
			role.add(roleItem);
			roles.add( role );
			
			role.addClickHandler(new ClickHandler(){
				@Override
				public void onClick(ClickEvent event) {
					showRoleEditor( new RoleEditor(RolesContainer.this, r, roleItem) );
				}
			});
			
			// Add remove button
			if (mayDeleted) {
				Anchor remove = new Anchor(constants.delete());
				role.add(remove);
				
				remove.addClickHandler(new ClickHandler() {
					@Override
					public void onClick(ClickEvent event) {
						event.stopPropagation();

						rolesList.remove(r);
						role.removeFromParent();
					}
				});
			}
		}

		public List<Role> getRoles() {
			return rolesList;
		}

		public void removeAllRoles() {
			rolesList.clear();
			roles.clear();
		}
	}
	
	private final ToweeConstants constants;
	private final ClientResource resource;

	// Main widgets
	private final UnorderedListWidget columns = new UnorderedListWidget();
	private final FlowPanel leftColumn = new FlowPanel();
	private final FlowPanel rightColumn = new FlowPanel();
	private final Button createButton;
	private final RolesContainer roles;
	public Role defaultRole;
	
	// Input boxes
	private final TextBox quest = new TextBox();
	private final TextBox answ = new TextBox();
	private final TextBox name = new TextBox();
	private final TextBox alias = new TextBox();
	private final TextBox url = new TextBox();
	private final TextBox desc = new TextBox();
	private final ListBox openness = new ListBox();
	
	@Inject
	public GroupsCreatorView(ToweeConstants constants, ClientResource resource) {
		this.constants = constants;
		this.resource = resource;

		// Create button
		createButton = new Button( constants.create() );
		roles = new RolesContainer();
		
		// Create columns
		createColumns();
	}
	
	private void createColumns() {
		// Set styles and add to panel
		columns.setStyleName(resource.css().lobbyGroupColumns());
		setStyleName(resource.css().absoluteMaximized());
		add(columns);
		
		// Create each columns
		createLeftColumn();
		createRightColumn();
	}

	private void createLeftColumn() {
		leftColumn.setStyleName(resource.css().lobbyGroupInColumn());
		ListItemWidget item = new ListItemWidget();
		item.setStyleName(resource.css().lobbyGroupColumn());
		item.add(leftColumn);
		columns.add(item);
		
		createDefaultAreas();
		createButtons();
		
		// Add create button
		leftColumn.add( createButton );
		createButton.setStyleName(resource.css().lobbyGroupCreateButton());
	}
	
	private void showRightColumn(Widget w) {
		rightColumn.clear();
		rightColumn.add(w);
	}
	
	private void createDefaultAreas() {
		leftColumn.add(createField(constants.group_name(), name));
		leftColumn.add(createField(constants.group_id(), alias));
		leftColumn.add(createField(constants.home_site(), url));
		leftColumn.add(createField(constants.description(), desc));
		leftColumn.add(createField(constants.control_quest(), quest));
		leftColumn.add(createField(constants.control_quest_answ(), answ));
		leftColumn.add(createField(constants.group_openness(), openness));
		
		openness.addItem(constants.group_fully_opened(), GroupOpenness.OPEN_GROUP+"");
		openness.addItem(constants.group_with_request(), GroupOpenness.WITH_REQUEST+"");
		openness.addItem(constants.group_closed(), GroupOpenness.HIDDEN_GROUP+"");
	}

	private Widget createField(String name, Widget w) {
		FlowPanel field = new FlowPanel();
		field.setStyleName(resource.css().lobbyGroupField());
		field.add(new ExtFlowPanel(name, resource.css().lobbyGroupFieldName()));
		field.add(w);
		return field;
	}
	
	private void createButtons() {
		createDefaultRoles();
		leftColumn.add(roles);
	}

	private void createRightColumn() {
		rightColumn.setStyleName(resource.css().lobbyGroupInColumn());
		ListItemWidget item = new ListItemWidget();
		item.setStyleName(resource.css().lobbyGroupColumn());
		item.add(rightColumn);
		columns.add(item);		
	}

	@Override
	public HasClickHandlers getCreateButton() {
		return createButton;
	}

	@Override
	public String getPrintName() {
		return name.getText();
	}

	@Override
	public String getAlias() {
		return alias.getText();
	}

	@Override
	public String getHomeURL() {
		return url.getText();
	}

	@Override
	public String getDescription() {
		return desc.getText();
	}

	@Override
	public GroupOpenness getOpenness() {
		return GroupOpenness.valueOf( openness.getValue( openness.getSelectedIndex() ) );
	}

	@Override
	public String getTestQuest() {
		return quest.getText();
	}

	@Override
	public String getTestAnsw() {
		return answ.getText();
	}

	@Override
	public List<Role> getRoles() {
		return roles.getRoles();
	}

	@Override
	public List<Pair<String, String>> getExtendedProfile() {
		return new ArrayList();
	}

	@Override
	public void questTooShort(int minLength) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void answTooShort(int minLength) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void nameTooShort(int minLength) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void aliasTooShort(int minLength) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void descTooShort(int minLength) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void emptyRole(Role r) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void invalidURL() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public boolean hasErrors() {
		return false;
	}

	@Override
	public void onStartCreating() {
		
	}

	@Override
	public void onErrorCreating() {
		Window.alert(":(");
	}

	@Override
	public void onEndCreating() {
		Window.alert("Группа создана");
		clearForm();
	}

	@Override
	public void clearForm() {
		quest.setValue("");
		answ.setValue("");
		name.setValue("");
		alias.setValue("");
		url.setValue("");
		desc.setValue("");

		// Remove all roles and add admin role
		roles.removeAllRoles();
		createDefaultRoles();
	}
	
	private void createDefaultRoles() {
		roles.addRole(new RoleImpl("admin", constants.admin(), true), false);
		Role userRole = new RoleImpl("user", constants.user(), false);
		userRole.addPermission(AccountPermission.CAN_ADD_DOCUMENT);
		userRole.addPermission(AccountPermission.CAN_ADD_MESSAGES);
		roles.addRole(userRole, true);
		defaultRole = userRole;
	}

	@Override
	public Role getDefaultRole() {
		return defaultRole;
	} 
}
